package chess;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;

/**
 * Classe Server (Servidor)
 * @author Beatriz Miho Anami
 * @author Lucas Rodrigues Anizelli
 * @author Marcos Okamura Rodrigues
 */
public class Server extends JFrame implements Constants{
	private ServerSocket serverSocket;
	private JPanel mainPanel;
	private JPanel gameInfoPanel;
	private JTextField port;
	private JTextArea chat;
	private JScrollPane chatPanel;
	private ArrayList<Socket> socketList = new ArrayList<Socket>();
	private JTable log;
	private JScrollPane logPanel;
	private JButton start;
	private JButton stop;
	
	private static final long serialVersionUID = 1L;
	
	/**
	 * Função principal da aplicação.
	 * @param args	argumentos
	 */
	public static void main(String[] args) {
		new Server();
	}
	
	/**
	 * Construtor do servidor.
	 */
	public Server() {
		this.initGUI();
	}
	
	/**
	 * Inicializa a GUI.
	 */
	private void initGUI() {
		this.setTitle("Chess - Server");
		this.setSize(500, 500);
		this.setResizable(false);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		mainPanel = new JPanel();
		mainPanel.setLayout(null);
		
		JLabel portLabel = new JLabel("Port");
		portLabel.setBounds(20, 20, 60, 20);
		port = new JTextField("5000");
		port.setBounds(100, 20, 60, 20);
		this.start = new JButton("Start");
		start.setBounds(250, 20, 80, 20);
		ServerActionListener listener = new ServerActionListener();
		start.addActionListener(listener);
		this.stop = new JButton("Stop");
		stop.setEnabled(false);
		stop.setBounds(350, 20, 80, 20);
		stop.addActionListener(listener);
		
		gameInfoPanel = new JPanel();
		gameInfoPanel.setLayout(null);
		gameInfoPanel.setBorder(BorderFactory.createTitledBorder("Game Info"));
		gameInfoPanel.setBounds(5, 5, 485, 85);
		gameInfoPanel.add(portLabel);
		gameInfoPanel.add(port);
		gameInfoPanel.add(start);
		gameInfoPanel.add(stop);
		
		chat = new JTextArea();
		chat.setEditable(false);
		chat.setLineWrap(true);
		chatPanel = new JScrollPane(this.chat);
		chatPanel.setBorder(BorderFactory.createTitledBorder("Chat"));
		chatPanel.setBounds(5, 100, 485, 200);
		
		String[][] chessLog = new String[100][3];
		String[] logTitle = {"", "White", "Black"};
		log = new JTable(chessLog, logTitle);
		log.setEnabled(false);
		log.setCellSelectionEnabled(false);
		for(int i = 0; i < 100; i++) {
			log.setValueAt(String.valueOf(i + 1), i, 0);
		}
		logPanel = new JScrollPane(this.log);
		log.getColumnModel().getColumn(0).setMaxWidth(30);
		logPanel.setBorder(BorderFactory.createTitledBorder("Log"));
		logPanel.setBounds(5, 310, 485, 150);
		
		mainPanel.add(logPanel);
		mainPanel.add(chatPanel);
		mainPanel.add(gameInfoPanel);
		
		this.add(mainPanel);
		this.setVisible(true);
	}
	
	/**
	 * Envia a mensagem recebida pelo servidor a todos os clientes
	 * @param message	messagem a ser enviada
	 */
	private void sendToEveryone(String message) {
		for(int i = 0; i < socketList.size(); i++){
			try {
				PrintWriter localWriter = new PrintWriter(socketList.get(i).getOutputStream());
				localWriter.println(message);
				localWriter.flush();
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * Classe ServerActionListener
	 * @author Beatriz Miho Anami
	 * @author Lucas Rodrigues Anizelli
	 * @author Marcos Okamura Rodrigues
	 */
	private class ServerActionListener implements ActionListener{
		/**
		 * Verifica se a ação foi executada.
		 * @paramEvent	evento da ação
		 */
		@Override
		public void actionPerformed(ActionEvent actionEvent){
			if(actionEvent.getSource() == start){
				start.setEnabled(false);
				stop.setEnabled(true);
				int portNumber = Integer.parseInt(port.getText());
				ConnectionThread connectionThread = new ConnectionThread(portNumber);
				connectionThread.start();
			} else {
				try{
					serverSocket.close();
					for(int i = 0; i < socketList.size(); i++){
						socketList.get(i).close();
					}
				} catch (IOException exception){
					exception.printStackTrace();
				}
				for(int i = 0; i < 100; i++) {
					log.setValueAt(null, i, 1);
					log.setValueAt(null, i, 2);
				}
				chat.setText("");
				start.setEnabled(true);
				stop.setEnabled(false);
			}
		}
	}
	
	/**
	 * Classe ConnectionThread
	 * @author Beatriz Miho Anami
	 * @author Lucas Rodrigues Anizelli
	 * @author Marcos Okamura Rodrigues
	 */
	private class ConnectionThread extends Thread{
		private int portNumber;
		private int playerCount;
		
		/**
		 * Construtor da ConnectionThread.
		 * @param portNumber	número da porta
		 */
		public ConnectionThread(int portNumber){
			this.playerCount = 0;
			this.portNumber = portNumber;
		}
		
		/**
		 * Envia as configurações pelo socket especificado.
		 * @param socket	socket da transmissão
		 */
		private void sendConfig(Socket socket){
			try{
				PrintWriter printWriter = new PrintWriter(socket.getOutputStream());
				if(playerCount < 2){
					printWriter.write("CONF;" + playerCount + '\n');
					playerCount++;
				} else {
					printWriter.write("CONF;-1\n");
				}
				printWriter.flush();
			} catch(IOException exception){
				exception.printStackTrace();
			}
		}
		
		
		/**
		 * Inicializa a thread.
		 */
		@Override
		public void run(){
			try {
				serverSocket = new ServerSocket(portNumber);
				while(true){
					Socket socket = null;
					try{
						socket = serverSocket.accept();
					} catch(SocketException exception){
						break;
					}
					socketList.add(socket);
					sendConfig(socket);
					ServerThread serverThread = new ServerThread(socket);
					serverThread.start();
				} 
			} catch(IOException exception){
				exception.printStackTrace();
			}
		}
	}
	
	/**
	 * Classe ServerThread
	 * @author Beatriz Miho Anami
	 * @author Lucas Rodrigues Anizelli
	 * @author Marcos Okamura Rodrigues
	 */
	private class ServerThread extends Thread {
		Socket socket;
		BufferedReader reader;
		
		/**
		 * Construtor da ServerThread.
		 * @param socket	socket da thread
		 */
		public ServerThread(Socket socket) {
			try {
				this.socket = socket;
				this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
		
		/**
		 * Inicializa a thread.
		 */
		@Override
		public void run() {
			String input;
			try {
				while((input = reader.readLine()) != null){
					if(input.substring(0, 4).equals("GAME")) {
						String[] inputSplit = input.split(";");
						int turn = Integer.parseInt(inputSplit[1]);
						int player = Integer.parseInt(inputSplit[2]) + 1;
						log.setValueAt(inputSplit[3], turn, player);
						sendToEveryone(input);
					} else {
						chat.append(input.substring(5) + '\n');
						sendToEveryone(input);
					}
				}
				socket.close();
				JOptionPane.showMessageDialog(mainPanel, "Client has closed");
			} catch(IOException exception) {
				exception.printStackTrace();
			}
		}
	}
}
